﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Data;
using System.Text.RegularExpressions;
using System.Text;
using Microsoft.MediaCenter;
using Microsoft.MediaCenter.UI;

using ClrExtensions;
using ClrExtensions.Net.Rest;

using WrapNetflix;

namespace MCEMediaManager
{
    public class NetflixPlugin : MediaPlugin, IMediaPlugin
    {
        #region Private Properties
        private VirtualList _SearchResultsDVDs;
        private VirtualList _SearchResultsInstant;
        private VirtualList _DiscoveryItemsInstant;
        private VirtualList _DiscoveryItemsDVDs;
        private NetflixConnection _CurrentNeflixConnection;
        private AccessToken _CurrentAccessToken;
        private User _CurrentUser;
        #endregion

        #region Constructors
        public NetflixPlugin()
        {
            try
            {
                _CurrentNeflixConnection = new NetflixConnection("r5h6brfudj34sfncapd2xrjh", "a6Zmz5rkzX");
                if (NetflixAccessToken == "") //Need to authenticate the user.
                {
                    RequestToken _CurrentRequestToken = _CurrentNeflixConnection.GenerateRequestToken();
                    if(Application.Current.MediaCenterEnvironment != null)
                        Application.Current.MediaCenterEnvironment.NavigateToPage(PageId.ExtensibilityUrl, _CurrentRequestToken.PermissionUrl);
                    _CurrentAccessToken = _CurrentRequestToken.ConvertToAccessToken();
                    NetflixUserID = _CurrentAccessToken.UserId;
                    NetflixAccessToken = _CurrentAccessToken.Token;
                    NetflixAccessTokenSecret = _CurrentAccessToken.TokenSecret;
                }
                else
                {
                    _CurrentAccessToken = new AccessToken(NetflixUserID, NetflixAccessToken, NetflixAccessTokenSecret);
                }

                _CurrentUser = new User(_CurrentAccessToken, _CurrentNeflixConnection);
            }
            catch(Exception ex)
            {
                Application.Current.MessageBox("Could not log in " + NetflixUsername + " to Netflix.  Please check the username and password to make sure it is correct. Error : " + ex.Message, "Netflix Login Failed", 5, false);
            }
        }
        #endregion

        #region Public Properties
        public override string Name
        {
            get
            {
                return "Netflix";
            }
        }

        public VirtualList SearchResultsDVDs
        {
            get
            {
                if (_SearchResultsDVDs == null)
                {
                    _SearchResultsDVDs = new VirtualList();
                    _SearchResultsDVDs.EnableSlowDataRequests = true;
                    _SearchResultsDVDs.RequestSlowDataHandler = new RequestSlowDataHandler(CompleteMovieItem);
                }
                return (this._SearchResultsDVDs);
            }
            set { this._SearchResultsDVDs = value; }
        }

        public VirtualList SearchResultsInstant
        {
            get
            {
                if (_SearchResultsInstant == null)
                {
                    _SearchResultsInstant = new VirtualList();
                    _SearchResultsInstant.EnableSlowDataRequests = true;
                    _SearchResultsInstant.RequestSlowDataHandler = new RequestSlowDataHandler(CompleteMovieItem);
                }
                return (this._SearchResultsInstant);
            }
            set { this._SearchResultsInstant = value; }
        }

        public VirtualList DiscoveryItemsDVDs
        {
            get
            {
                if (_DiscoveryItemsDVDs == null)
                {
                    _DiscoveryItemsDVDs = new VirtualList();
                    _DiscoveryItemsDVDs.EnableSlowDataRequests = true;
                    _DiscoveryItemsDVDs.RequestSlowDataHandler = new RequestSlowDataHandler(CompleteMovieItem);
                }
                return (this._DiscoveryItemsDVDs);
            }
            set { this._DiscoveryItemsDVDs = value; }

        }

        public VirtualList DiscoveryItemsInstant
        {
            get
            {
                if (_DiscoveryItemsInstant == null)
                {
                    _DiscoveryItemsInstant = new VirtualList();
                    _DiscoveryItemsInstant.EnableSlowDataRequests = true;
                    _DiscoveryItemsInstant.RequestSlowDataHandler = new RequestSlowDataHandler(CompleteMovieItem);
                }
                return (this._DiscoveryItemsInstant);
            }
            set { this._DiscoveryItemsInstant = value; }
        }

        public string NetflixUserID
        {
            get
            {
                string value = Application.Current.DataStore.GetString("NetflixUserID", "");
                return value;
            }
            set
            {
                Application.Current.DataStore.SetValue("NetflixUserID", value);
            }
        }

        public string NetflixUsername
        {
            get
            {
                string value = Application.Current.DataStore.GetString("NetflixUsername", "username@domain.com");
                return value;
            }
            set
            {
                Application.Current.DataStore.SetValue("NetflixUsername", value);
            }
        }

        public string NetflixPassword
        {
            get
            {
                string value = (string)Application.Current.DataStore.GetString("NetflixPassword", "password");
                return value;
            }
        }

        public string NetflixAccessToken
        {
            get
            {
                string value = (string)Application.Current.DataStore.GetString("NetflixAccessToken", "");
                return value;
            }
            set
            {
                Application.Current.DataStore.SetValue("NetflixAccessToken", value);
            }
        }

        public string NetflixAccessTokenSecret
        {
            get
            {
                string value = (string)Application.Current.DataStore.GetString("NetflixAccessTokenSecret", "");
                return value;
            }
            set
            {
                Application.Current.DataStore.SetValue("NetflixAccessTokenSecret", value);
            }
        }

        public NetflixConnection CurrentNetflixConnection
        {
            get
            {
                return _CurrentNeflixConnection;
            }
            set
            {
                _CurrentNeflixConnection = value;
            }
        }

        public User CurrentUser
        {
            get
            {
                return _CurrentUser;
            }
            set
            {
                _CurrentUser = value;
            }
        }

        public AccessToken CurrentAccessToken
        {
            get { return _CurrentAccessToken; }
            set { _CurrentAccessToken = value; }
        }

        public override bool HasDiscoveryInterface
        {
            get { return true; }
        }

        public override bool HasSearchInterface
        {
            get { return true; }
        }

        public override bool HasRatings
        {
            get { return true; }
        }

        public override List<string> InitializeFilters()
        {
            List<string> tempFilters = new List<string>();
            tempFilters.Add("DVDs");
            tempFilters.Add("Watch Instantly");
            return tempFilters;
        }
        #endregion

        #region Methods
        public NetflixMediaItem ConvertMovieToMediaItem(CatalogTitle movie)
        {
            NetflixMediaItem newMediaItem = new NetflixMediaItem();
            newMediaItem.ID = NetflixPlugin.GetIDFromURL(movie.Id);
            newMediaItem.Name = movie.Title;
            newMediaItem.Description = movie.SynopsisLink.Source.Value;
            newMediaItem.URL = movie.WebPageLink;
            newMediaItem.MovieData = movie;
            newMediaItem.Type = MediaItemType.Movie;
            if (movie.Genres.Any(genre => genre == "Television"))
                newMediaItem.Type = MediaItemType.TV;
            newMediaItem.Plugin = this;

            return newMediaItem;
        }

        public void AddMovieToSearchResults(object obj)
        {
            try
            {
                if (((CatalogTitle)obj).DeliveryFormats.Contains("instant"))
                    SearchResultsInstant.Add(ConvertMovieToMediaItem((CatalogTitle)obj));
                else if (((CatalogTitle)obj).DeliveryFormats.Contains("DVD"))
                    SearchResultsDVDs.Add(ConvertMovieToMediaItem((CatalogTitle)obj));
                SearchResultsCount++;
            }
            catch (Exception ex)
            {
                Application.Current.MessageBox("AddMovieToSearchResults : " + ex.Message, "Netflix Plugin Error", 3, false);
            }
        }

        public void CompleteMovieItem(VirtualList list, int index)
        {
            //NetflixMediaItem item = (NetflixMediaItem)list[index];
            //TryLabel:
            //try
            //{
            //    item.Description = item.MovieData.SynopsisLink.FetchXml().Value;
            //}
            //catch (Exception ex)
            //{
            //    System.Threading.Thread.Sleep(500);
            //    goto TryLabel;
            //}
        }

        public override IList GetSearchResults(string filter)
        {
            if (filter == "DVDs")
                return (IList)_SearchResultsDVDs;
            else if (filter == "Watch Instantly")
                return (IList)_SearchResultsInstant;
            else
                return (IList)_SearchResultsDVDs;
        }

        public override void SearchByName(object obj)
        {
            try
            {
                SearchResultsCount = 0;
                SearchArgs args = (SearchArgs)obj;
                CatalogTitleCollection movies = _CurrentNeflixConnection.Catalog.TitleSearch(args.SearchPage.SearchText, 20, TitleExpansions.All);
                CurrentUser.AddUserRatings(ref movies);
                Microsoft.MediaCenter.UI.Application.DeferredInvoke(delegate { SearchResultsDVDs.Clear(); });
                Microsoft.MediaCenter.UI.Application.DeferredInvoke(delegate { SearchResultsInstant.Clear(); });
                foreach (CatalogTitle movie in movies)
                {
                    Microsoft.MediaCenter.UI.Application.DeferredInvoke(new DeferredHandler(AddMovieToSearchResults), movie);
                }
            }
            catch (Exception ex)
            {
                Application.Current.MessageBox("SearchByName : " + ex.Message, "Netflix Plugin Error", 3, false);
            }
        }

        public override IList GetDiscoveryItems(string filter)
        {
            if (filter == "DVDs")
                return (IList)_DiscoveryItemsDVDs;
            else if (filter == "Watch Instantly")
                return (IList)_DiscoveryItemsInstant;
            else
                return (IList)_DiscoveryItemsDVDs;
        }

        public override void CreateDiscoveryItems(object obj)
        {
            try
            {
                DiscoveryItemsCount = 0;
                SearchArgs args = (SearchArgs)obj;
                RecommendationCollection movies = _CurrentUser.GetRecommedations(20, TitleExpansions.All);
                CurrentUser.AddUserRatings(ref movies);
                Microsoft.MediaCenter.UI.Application.DeferredInvoke(delegate { DiscoveryItemsDVDs.Clear(); });
                Microsoft.MediaCenter.UI.Application.DeferredInvoke(delegate { DiscoveryItemsInstant.Clear(); });
                foreach (RecommendationItem movie in movies)
                {
                    Microsoft.MediaCenter.UI.Application.DeferredInvoke(new DeferredHandler(AddMovieToDiscoveryItems), (CatalogTitle) movie);
                }
            }
            catch (Exception ex)
            {
                Application.Current.MessageBox("CreateDiscoveryItems : " + ex.Message, "Netflix Plugin Error", 3, false);
            }
        }

        public void AddMovieToDiscoveryItems(object obj)
        {
            try
            {
                if (((RecommendationItem)obj).DeliveryFormats.Contains("instant"))
                    DiscoveryItemsInstant.Add(ConvertMovieToMediaItem((CatalogTitle)obj));
                else if (((RecommendationItem)obj).DeliveryFormats.Contains("DVD"))
                    DiscoveryItemsDVDs.Add(ConvertMovieToMediaItem((CatalogTitle)obj));
                DiscoveryItemsCount++;
            }
            catch (Exception ex)
            {
                Application.Current.MessageBox("AddMovieToDiscoveryItems : " + ex.Message, "Netflix Plugin Error", 3, false);
            }
        }

        public override void OnDetailsActionInvoked(DetailsCommand command)
        {
            NetflixMediaItem theNetflixMI = ((NetflixMediaItem)command.Owner);
            switch (command.Type)
            {
                case "AddDVDQueue":
                    try
                    {
                        CurrentUser.DiscQueue.Add(theNetflixMI.MovieData.Id);
                        CurrentUser.DiscQueue.Refresh();
                    }
                    catch { }
                    theNetflixMI.UpdateCommands();
                    break;
                case "RemoveDVDQueue":
                    try
                    {
                        CurrentUser.DiscQueue.Remove(command.Value);
                        CurrentUser.DiscQueue.Refresh();
                    }
                    catch { }
                    theNetflixMI.UpdateCommands();
                    break;
                case "AddInstantQueue":
                    try
                    {
                        CurrentUser.InstantQueue.Add(theNetflixMI.MovieData.Id);
                        CurrentUser.InstantQueue.Refresh();
                    }
                    catch { }
                    theNetflixMI.UpdateCommands();
                    break;
                case "RemoveInstantQueue":
                    try
                    {
                        CurrentUser.InstantQueue.Remove(command.Value);
                        CurrentUser.InstantQueue.Refresh();
                    }
                    catch { }
                    theNetflixMI.UpdateCommands();
                    break;
                case "PlayEpisodes":
                case "PlayStream":
                default:
                    base.OnDetailsActionInvoked(command);
                    break;
            }
        }
        public static string GetIDFromURL(string url)
        {
            string returnValue = "";

            returnValue = Regex.Match(url, @"([\d]+)$").Value;

            return returnValue;
        }
        #endregion

    }
    public class NetflixMediaItem : MediaItem, IMediaItem
    {
        private CatalogTitle _MovieData;
        private string _ID;
        private WrapNetflix.Rating _UserRating;

        public CatalogTitle MovieData
        {
            get { return (this._MovieData); }
            set { this._MovieData = value; }
        }

        public string ID
        {
            get { return (this._ID); }
            set { this._ID = value; }
        }

        public override Single UserRating
        {
            get
            {
                return Convert.ToSingle(MovieData.UserRating);
            }
            set
            {
                MovieData.UserRating = (Rating) Enum.Parse(typeof(Rating), Convert.ToInt32(value).ToString());
                ((NetflixPlugin)Plugin).CurrentUser.SetRating(MovieData.TitleRef, Convert.ToInt32(value).ToString());
                FirePropertyChanged("UserRating");
            }
        }

        public override string ListDisplayProperties
        {
            get
            {
                return @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Segeo;}}{\colortbl ;\red255\green255\blue255;}{\*\generator Msftedit 5.41.21.2508;}\viewkind4\uc1\pard\cf1\b\f0\fs28 Name\b0  : " + Name + @"\par\b Genres\b0 : " + ((MovieData.Genres.Count > 0) ? MovieData.Genres.Join(", ") : "") + @"\par\b Year\b0 : " + MovieData.ReleaseYear.ToString() + @"\tab\tab \b Avg. Rating\b0 : " + MovieData.AverageRating + @" \par}";
            }
        }

        public override ArrayListDataSet Commands
        {
            get
            {
                if (_Commands == null)
                {
                    UpdateCommands();
                }
                return _Commands;
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (_MovieData != null)
                _MovieData = null;
        }

        public void UpdateCommands()
        {
            ArrayListDataSet tempCommands = new ArrayListDataSet(this);
            bool showAddQueue;
            if (this.MovieData.DeliveryFormats.Contains("DVD"))
            {
                showAddQueue = true;
                foreach (QueueItem qi in ((NetflixPlugin)Plugin).CurrentUser.DiscQueue)
                {
                    if (this.ID == NetflixPlugin.GetIDFromURL(qi.Id))
                    {
                        tempCommands.Add(Plugin.CreateCommand(this, "Remove from DVD Queue", "RemoveDVDQueue", qi.Id.ToString()));
                        showAddQueue = false;
                        break;
                    }
                }
                if (showAddQueue)
                    tempCommands.Add(Plugin.CreateCommand(this, "Add to DVD Queue", "AddDVDQueue", this.MovieData.Id.ToString()));
            }
            if (this.MovieData.DeliveryFormats.Contains("instant"))
            {
                showAddQueue = true;
                foreach (QueueItem qi in ((NetflixPlugin)Plugin).CurrentUser.InstantQueue)
                {
                    if (this.ID == NetflixPlugin.GetIDFromURL(qi.Id))
                    {
                        tempCommands.Add(Plugin.CreateCommand(this, "Remove from Instant Queue", "RemoveInstantQueue", qi.Id.ToString()));
                        showAddQueue = false;
                        break;
                    }
                }
                if (showAddQueue)
                    tempCommands.Add(Plugin.CreateCommand(this, "Add to Instant Queue", "AddInstantQueue", this.MovieData.Id.ToString()));
            }
            _Commands = tempCommands;
            FirePropertyChanged("Commands");
        }
    }
}
